//
//  XCBluetoothManager.swift
//  RXSwiftDemo1
//
//  Created by mac on 2025/3/4.
//

import Foundation
import RxSwift
import CoreBluetooth

enum ConnectionState: Equatable {
    case disconnected
    case connecting
    case connected
    case failed(Error)
    
    static func == (lhs: ConnectionState, rhs: ConnectionState) -> Bool {
        switch (lhs, rhs) {
        case (.disconnected, .disconnected),
            (.connecting, .connecting),
            (.connected, .connected):
            return true
        case let (.failed(error1), .failed(error2)):
            return error1.localizedDescription == error2.localizedDescription
        default:
            return false
        }
    }
}


class XCBluetoothManager {
    static let instance = XCBluetoothManager()
    
    private static let filterPeripheraName:[String] = ["BT_ring","Zikr","SC01"] //自家产品前缀
    private let targetServiceUUID = CBUUID(string: "D0FF") //根据这个服务id扫描特征
    private let writeUUID = CBUUID(string: "D004")
    private let readAndNotifyUUID = CBUUID(string: "D002")
    
    public let centerManager = CBCentralManager(delegate: nil, queue: nil)
    private let disposeBag = DisposeBag()
    
    //蓝牙状态
    private let bluetoothStateSubject = BehaviorSubject<BluetoothState>(value: .unknown)
    var bluetoothStateObservable: Observable<BluetoothState> {
        return bluetoothStateSubject.asObservable()
    }
    
    //过滤的设备
    private let discoveredPeripheralsSubject = ReplaySubject<[XCBlueToothModel]>.create(bufferSize: 1)
    private var discoveredPeripherals = [XCBlueToothModel]()
    
    var discoveredPeripheralsObservable: Observable<[XCBlueToothModel]> {
        return discoveredPeripheralsSubject.asObservable()
    }
    
    //当前连接的设备
    public var connectedPeripheral: CBPeripheral?
    
    // 读与写特征
    private var writeCharacteristic: CBCharacteristic?
    private var readCharacteristic: CBCharacteristic?
    
    // 当前连接设备模型
    public var connectedBluetoothModel: XCBlueToothModel?
    
    private let rssiThreshold = -80 // 信号强度阈值
    
    private lazy var reconnectManager = XCReconnectManager.instance
    
    private init() {
        
        centerManager.rx.centralManagerDidUpdateState
            .bind(to: bluetoothStateSubject)
            .disposed(by: disposeBag)
    }
    
    // 扫描设备，并过滤
    func scanForPeripherals(withFilter filterWords: [String] = filterPeripheraName) -> Observable<[XCBlueToothModel]> {
        centerManager.scanForPeripherals(withServices: nil, options: nil)
        centerManager.rx.didDiscoverPeripheral
            .subscribe(onNext: { [weak self] (peripheral, advertisementData, rssi) in
                guard let self = self else { return }
                
                if let peripheralName = peripheral.name {
                    for prefix in filterWords {
                        if peripheralName.hasPrefix(prefix) {
                            let model = XCBlueToothModel()
                            model.uuid = peripheral.identifier
                            model.peripheral = peripheral
                            model.rssi = Int(truncating: rssi)
                            model.setAdvData(from: advertisementData)
                            
                            if let isConnectable = advertisementData["kCBAdvDataIsConnectable"] as? NSNumber {
                                model.isConnected = isConnectable.boolValue
                            }
                            
                            if let deviceName = advertisementData["kCBAdvDataLocalName"] as? String, !deviceName.isEmpty {
                                model.deviceName = deviceName
                            }else {
                                model.deviceName = peripheralName
                            }
                            
                            if let manufacturerData = advertisementData["kCBAdvDataManufacturerData"] as? Data {
                                let tempAddress = convertToNSString(data: manufacturerData)
                                var tempArr = [String]()
                                
                                if let tempAddress = tempAddress, tempAddress.count == 16 {
                                    for i in stride(from: 7, to: 1, by: -1) {
                                        let str = tempAddress[tempAddress.index(tempAddress.startIndex, offsetBy: i * 2)..<tempAddress.index(tempAddress.startIndex, offsetBy: (i * 2) + 2)]
                                        tempArr.append(String(str))
                                    }
                                }
                                
                                let macAddress = tempArr.joined(separator: ":").uppercased()
                                model.macAddress = macAddress
                                
                                if !self.discoveredPeripherals.contains(where: { $0.macAddress == macAddress }) {
                                    self.discoveredPeripherals.append(model)
                                    self.discoveredPeripheralsSubject.onNext(self.discoveredPeripherals)
                                }
                            }
                        }
                    }
                }
            })
            .disposed(by: disposeBag)
        
        return discoveredPeripheralsSubject.asObservable()
    }
    
    // 连接设备
    func connect(to peripheral: CBPeripheral) -> Observable<ConnectionState> {
        
        return Observable.create { [weak self] observer in
            guard let self = self else {
                observer.onCompleted()
                return Disposables.create()
            }
            
            self.centerManager.connect(peripheral, options: nil)
            observer.onNext(.connecting)
            
            let didConnect = self.centerManager.rx.didConnectPeripheral
                .filter { $0 == peripheral }
                .flatMap({ connectedPeripheral-> Observable<[CBService]> in
                    self.connectedPeripheral = connectedPeripheral
                    self.connectedPeripheral?.discoverServices([self.targetServiceUUID])
                    return self.connectedPeripheral!.rx.didDiscoverServices
                })
                .flatMap({ services-> Observable<(CBService, [CBCharacteristic]?)> in
                    guard let service = services.first(where: { $0.uuid == self.targetServiceUUID }) else {
                        observer.onNext(.failed(NSError(domain: "Service not found", code: -1, userInfo: nil)))
                        return Observable.empty()
                    }
                    self.connectedPeripheral?.discoverCharacteristics([self.writeUUID, self.readAndNotifyUUID], for: service)
                    return self.connectedPeripheral!.rx.didDiscoverCharacteristicsForService
                        .filter { $0.0 == service }
                })
                .do(onNext: { (service, characteristics) in
                    characteristics?.forEach { characteristic in
                        if characteristic.uuid == self.writeUUID {
                            self.writeCharacteristic = characteristic
                        } else if characteristic.uuid == self.readAndNotifyUUID {
                            self.readCharacteristic = characteristic
                            self.connectedPeripheral?.setNotifyValue(true, for: characteristic)
                        }
                    }
                    
                    // 检查是否已保存读写特征
                    if self.writeCharacteristic != nil && self.readCharacteristic != nil {
                        if let peripheral = self.connectedPeripheral {
                            if let model = self.discoveredPeripherals.first(where: { $0.uuid == peripheral.identifier }) {
                                self.connectedBluetoothModel = model
                                self.saveLastConnectedPeripheral(model)
                            }
                        }
                        
                        observer.onNext(.connected)
                        observer.onCompleted()
                        self.reconnectManager.stopMonitoringRSSI()
                        self.reconnectManager.reconnectAttempts = 0
                    }
                })
                .map { _ in .connected }
                .subscribe(observer)
            
            let didFailToConnect = self.centerManager.rx.didFailToConnectPeripheral
                .filter { $0.0 == peripheral }
                .do(onNext: {(periphral, error) in
                    self.reconnectManager.targetPeripheral = periphral
                    self.reconnectManager.reconnectWithStrategy()
                })
                .subscribe(onNext: { (failedPeripheral, error) in
                    self.resetConnectionState()
                    observer.onNext(.disconnected)
                    observer.onCompleted()
                },onError: {error in
                    observer.onNext(.failed(error))
                    observer.onCompleted()
                })
            
            let didDisconnectPeripheral = self.centerManager.rx.didDisconnectPeripheral
                .filter({ $0.0 == peripheral })
                .do(onNext: {(periphral, error) in
                    if error != nil {
                        self.reconnectManager.targetPeripheral = periphral
                        self.reconnectManager.reconnectWithStrategy()
                    }else {
                        self.reconnectManager.stopMonitoringRSSI()
                    }
                })
                .subscribe(onNext: { (failedPeripheral, error) in
                    self.resetConnectionState()
                    observer.onNext(.disconnected)
                    observer.onCompleted()
                },onError: {error in
                    observer.onNext(.failed(error))
                    observer.onCompleted()
                })
            
            let peripheralDidUpdateRSSI = self.connectedPeripheral?.rx.didReadRSSI
                .filter({ [weak self] in ($0.0.identifier == self?.connectedPeripheral?.identifier) && ($0.1.intValue > self!.rssiThreshold ) })
                .subscribe(onNext: { (peripheral,_,_) in
                    self.centerManager.connect(peripheral)
                    self.reconnectManager.stopMonitoringRSSI()
                })
            
            
            return Disposables.create {
                didConnect.dispose()
                didFailToConnect.dispose()
                didDisconnectPeripheral.dispose()
                peripheralDidUpdateRSSI?.dispose()
            }
        }
    }
    
    // 断开连接
    func disconnect() ->Observable<CBPeripheralState> {
        
        return Observable.create { [weak self]  observer in
            guard let self = self, let peripheral = self.connectedPeripheral else {
                observer.onNext(.disconnected)
                observer.onCompleted()
                return Disposables.create()
            }
            centerManager.cancelPeripheralConnection(peripheral)
            resetConnectionState()
            centerManager.rx.didDisconnectPeripheral
                .map({ $0.0.state })
                .subscribe(onNext: { state in
                    observer.onNext(state)
                    observer.onCompleted()
                },onError: {error in
                    observer.onError(error)
                })
                .disposed(by: disposeBag)
            
            return Disposables.create()
        }
        
        
    }
    
    // 读取数据
    func readValue() -> Observable<Data> {
        guard let readCharacteristic = self.readCharacteristic else {
            return Observable.error(NSError(domain: "Characteristic not found", code: -1, userInfo: nil))
        }
        
        return self.connectedPeripheral!.rx.didUpdateValueForCharacteristic
            .compactMap { $0.value }
    }
    
    
    // 写入数据
    func writeValue(_ data: Data, type: CBCharacteristicWriteType) -> Observable<Void> {
        guard let writeCharacteristic = self.writeCharacteristic else {
            return Observable.error(NSError(domain: "Characteristic not found", code: -1, userInfo: nil))
        }
        
        return Observable.create { [weak self] observer in
            guard let self = self, let peripheral = self.connectedPeripheral else {
                observer.onError(NSError(domain: "Peripheral not connected", code: -1, userInfo: nil))
                return Disposables.create()
            }
            
            // 获取 MTU 大小
            let mtuSize = peripheral.maximumWriteValueLength(for: type)
            let chunkSize = mtuSize - 3 // 计算安全分包大小（MTU - 3）
            
            var offset = 0
            let dispatchQueue = DispatchQueue(label: "com.example.writeValueQueue") // 串行队列
            
            func sendNextChunk() {
                dispatchQueue.async {
                    guard offset < data.count else {
                        // 所有数据发送完成
                        observer.onNext(())
                        observer.onCompleted()
                        return
                    }
                    
                    let end = min(offset + chunkSize, data.count)
                    let chunk = data.subdata(in: offset..<end) // 截取数据
                    
                    // 发送数据
                    DispatchQueue.main.async { // 确保在主线程上执行写入操作
                        peripheral.writeValue(chunk, for: writeCharacteristic, type: type)
                        
                        if type == .withResponse {
                            // 监听写入响应
                            let didWriteValue = peripheral.rx.didWriteValueForCharacteristic
                                .filter { $0.0 == writeCharacteristic }
                                .take(1) // 只监听一次
                                .subscribe(onNext: { (characteristic, error) in
                                    if let error = error {
                                        observer.onError(error)
                                    } else {
                                        offset += chunkSize
                                        dispatchQueue.asyncAfter(deadline: .now() + 0.2) {
                                            sendNextChunk()
                                        }
                                    }
                                })
                            
                            // 将订阅添加到 disposeBag
                            didWriteValue.disposed(by: self.disposeBag)
                        } else {
                            offset += chunkSize
                            dispatchQueue.asyncAfter(deadline: .now() + 0.2) {
                                sendNextChunk()
                            }
                        }
                    }
                }
            }
            
            sendNextChunk()
            
            return Disposables.create()
        }
    }
    
}

extension XCBluetoothManager {
    
    // 重置连接状态
    private func resetConnectionState() {
        self.connectedPeripheral = nil
        self.writeCharacteristic = nil
        self.readCharacteristic = nil
        self.connectedBluetoothModel = nil
    }
    
    // 缓存上一次连接的设备
    private func saveLastConnectedPeripheral(_ model: XCBlueToothModel) {
        do {
            let data = try JSONEncoder().encode(model)
            UserDefaults.standard.set(data, forKey: "LastConnectedPeripheral")
        } catch {
            print("Failed to save last connected peripheral: \(error)")
        }
    }
    
    // 读取上一次的设备
    public func loadLastConnectedPeripheral() -> XCBlueToothModel? {
        guard let data = UserDefaults.standard.data(forKey: "LastConnectedPeripheral") else {
            return nil
        }
        do {
            return try JSONDecoder().decode(XCBlueToothModel.self, from: data)
        } catch {
            print("Failed to load last connected peripheral: \(error)")
            return nil
        }
    }
    
    func convertToNSString(data: Data) -> String? {
        let bytes = [UInt8](data)
        var result = ""
        
        for byte in bytes {
            result.append(String(format: "%02lx", byte))
        }
        
        return result
    }
    
    // 根据 formatter 格式获取目前的时间字符串
    func getDateWithFormatterString(formatter: String) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = formatter.isEmpty ? "yyyyMMddHHmmss" : formatter
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        dateFormatter.calendar = Calendar(identifier: .gregorian)
        return dateFormatter.string(from: Date())
    }
}
